package tpase.KeyServer.Handler;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Field;
import it.unisa.dia.gas.jpbc.Pairing;
import tpase.KeyServer.DO.KeyShare;
import tpase.KeyServer.DO.UserAuth;
import tpase.KeyServer.Service.KeyServerStartService;
import tpase.KeyServer.VO.*;
import tpase.util.Util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@ChannelHandler.Sharable
public class ServerStartHandler extends ChannelInboundHandlerAdapter {

    private String jpbcPath;
    private String paramPath;
    private String publicParamPath;
    private String secretKeyPath;
    private Pairing pairing;
    PublicParam publicParam;
    private Element x, s_i;
    private Address address;
    private boolean isDebug;
    private String  severid;
    private KeyServerStartService keyServerStartService;

    public ServerStartHandler(KeyServerStartService keyServerStartService, String paramPath, Address address, boolean isDebug) {
        this.keyServerStartService = keyServerStartService;
        this.paramPath = paramPath;
        this.severid = "sever"+paramPath.charAt(paramPath.length()-1);

        jpbcPath = paramPath + "/a.properties";
        publicParamPath = paramPath + "/publicParam.json";
        secretKeyPath = paramPath + "/secretKey.json";
        this.address = address;
        this.isDebug = isDebug;
        loadParameters();
    }

    private void loadParameters() {
        publicParam = Util.jsonFileToObj(publicParamPath, PublicParam.class);
        pairing = Util.getPairing(jpbcPath);
    }

    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Message request = (Message) msg;
        int nowStatusCode = request.getStatusCode();

        switch (nowStatusCode) {
            case StatusCode.REGISTER_REQUEST:
                registerResponse(ctx, request);
                break;
            case StatusCode.AUTH_STORE_REQUEST:
                authStoreResponse(ctx, request);
                break;
            case StatusCode.LOGIN_REQUEST:
                loginResponse(ctx, request);
                break;
            case StatusCode.KG_REQUEST:
                KGResponse(ctx, request);
                break;
        }
    }

    /**
     * 用户登录，返回 用户认证信息份额Y_1i 给 用户
     * @param ctx
     * @param request
     */
    private void loginResponse(ChannelHandlerContext ctx, Message request) {
        Map<String, byte[]> map = request.getMap();
        String uid = new String(map.get("uid"));

        if (!keyServerStartService.hasUid(uid)) {
            ctx.writeAndFlush(new Message(StatusCode.LOGIN_NO_UID_RESPONSE));
            return;
        }

        loadKeyShare(uid);

        Element x_pwd = pairing.getG1().newElementFromBytes(map.get("x_pwd"));
        Element Y_1i = Util.getY1iWithoutLambda(uid, x_pwd, pairing, s_i);
        Message response = new Message(StatusCode.LOGIN_RESPONSE);
        response.getMap().put("x", x.toBytes());
        response.getMap().put("Y_1i", Y_1i.toBytes());

        ctx.writeAndFlush(response);
    }

    private void KGResponse(ChannelHandlerContext ctx, Message request) {
        Map<String, byte[]> map = request.getMap();
        String uid = new String(map.get("uid"));

        if (!keyServerStartService.hasUid(uid)) {
            ctx.writeAndFlush(new Message(StatusCode.KG_NO_UID_RESPONSE));
            return;
        }

        loadKeyShare(uid);

        Element x_pwd = pairing.getG1().newElementFromBytes(map.get("x_pwd"));
        Element com_m = pairing.getZr().newElementFromBytes(map.get("com_m"));
        Message response = new Message(StatusCode.KG_RESPONSE);
        List<byte[]> idList = (List<byte[]>) Util.jsonToObject(new String(map.get("idList")), List.class, byte[].class);

        Element lambda_0iT = Util.getLambda_0iT(x, idList, pairing);
        Element Y_1i = Util.getY1iWithoutLambda(uid, x_pwd, pairing, s_i);
        Element Y_2i = Util.getY2iWithoutLambda(uid, com_m, pairing, s_i);
        getY2i(lambda_0iT, uid, com_m);

        //查询auth
        Element auth_uid = pairing.getZr().newElementFromBytes(keyServerStartService.getUserAuth(uid));
        byte[] c_Y2i = Util.encrypt(auth_uid.toString(), Y_2i.toBytes());

        // vi, ci = H(H_xpwd_uid^si, H_xpwd_uid, \gamma_tsi, gT, ti, ti2), ui = vi - ci * si, ti = e^vi, ti' = g^vi
        Element vi = pairing.getZr().newRandomElement();
        Element H_xpwd_uid = Y_1i.duplicate().powZn(s_i.duplicate().invert());
        Element gamma_tsi = pairing.getGT().newElementFromBytes(publicParam.getMap().get("gamma_ts" + x.toString()));
        Element gT = pairing.getGT().newElementFromBytes(publicParam.getMap().get("gT"));
        Element ti = H_xpwd_uid.duplicate().powZn(vi);
        Element ti2 = gT.duplicate().powZn(vi);

        // Y_1i, pairing.pairing(x_pwd, H_2_uid), \gamma_tsi, g1, ti
        String s = Y_1i.toString() + H_xpwd_uid.toString() + gamma_tsi.toString() + gT.toString() + ti.toString() + ti2.toString();
        Element ci = Util.hash(pairing.getZr(), s);
        Element ui = vi.duplicate().sub(ci.duplicate().mulZn(s_i));

        response.getMap().put("ci", ci.toBytes());
        response.getMap().put("ui", ui.toBytes());
        response.getMap().put("x", x.toBytes());

        response.getMap().put("Y_1i", Y_1i.toBytes());
        response.getMap().put("c_Y2i", c_Y2i);


//        try {
//            File tempfile = new File("src/main/resources/config/temp.txt");
//            BufferedWriter bw = new BufferedWriter(new FileWriter(tempfile, true));
//            bw.write("\t收到(Y_1i, c_Y2i,(ci, vi,x))，来自服务器"+address.toString()+"：\n");
////            bw.write("\t\tY1i："+Y_1i.toString()+"\n\t\tc_Y2i："+c_Y2i.toString()+"\n\t\t(ci, vi,x)：("+ci+","+vi+","+x+")\n");
//            bw.flush();
//            bw.close();
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }

        if (isDebug) {
            System.out.printf("[第1轮]  用户[admin]  <---(Y_1i, c_Y2i,(ci, vi,x))----  服务器[%s]：\n\tY1i[%s]\n\tc_Y2i[%s]\n\t验证信息(ci[%s], vi[%s], x[%s]) \n",
                    address.toString(), Y_1i, Arrays.toString(c_Y2i), ci, vi, x);
        }

        ctx.writeAndFlush(response);
    }

    private void authStoreResponse(ChannelHandlerContext ctx, Message request) {
        Element auth_uid_i = pairing.getZr().newElementFromBytes(request.getMap().get("auth_uid_i"));
        String uid = new String(request.getMap().get("uid"));

        Message response;

        // 该 uid 是否已经注册过
        if (keyServerStartService.hasUid(uid)) {
            response = new Message(StatusCode.AUTH_STORE_FAIL_RESPONSE);
        } else {
            UserAuth userAuth = new UserAuth(uid, auth_uid_i.toBytes());
            keyServerStartService.storeAuth(userAuth);
            response = new Message(StatusCode.AUTH_STORE_SUCCESS_RESPONSE);
        }
        ctx.writeAndFlush(response);
    }

    /**
     * 从数据库读取 x 和 s_i
     * @param uid
     */
    private void loadKeyShare(String uid) {
        KeyShare keyShare = keyServerStartService.getKeyShare(uid);
        x = pairing.getZr().newElement(new BigInteger(keyShare.getKeyServerId()));
        s_i = pairing.getZr().newElementFromBytes(keyShare.getSi());
    }
    private void registerResponse(ChannelHandlerContext ctx, Message msg) {
        Map<String, byte[]> map = msg.getMap();
        String uid = new String(map.get("uid"));

        if (keyServerStartService.hasUid(uid)) {
            ctx.writeAndFlush(new Message(StatusCode.REGISTER_ALREADY_EXISTING));
            return;
        }

        loadKeyShare(uid);

        Element x_pwd = pairing.getG1().newElementFromBytes(map.get("x_pwd"));
        List<byte[]> idList = Util.jsonToObject(new String(map.get("idList")), List.class, byte[].class);

        Element lambda_0iT = Util.getLambda_0iT(x, idList, pairing);
        Element Y_1i = getY1i(lambda_0iT, uid, x_pwd);

        Message response = new Message(StatusCode.REGISTER_RESPONSE);
        response.getMap().put("Y_1i", Y_1i.toBytes());


        if (isDebug) {
            System.out.printf("[第1轮] 用户[admin]  <---Y_1i----  服务器[%s]：\n\tY1i[%s]\n",
                    address.toString(), Y_1i);
        }

        ctx.writeAndFlush(response);
    }

    private Element getY1i(Element lambda_0iT, String uid, Element x_pwd) {
        Element H_2_uid = Util.hash(pairing.getG2(), uid);
        // e(x_pwd, H_2(uid))^{lambda_0iT * s_i}
        return pairing.pairing(x_pwd, H_2_uid).powZn(lambda_0iT.duplicate().mulZn(s_i));
    }

    private Element getY2i(Element lambda0iT, String uid, Element com_m) {
        Element H_4_uid_com_m = Util.hash(pairing.getG2(), uid + com_m.toString());
        H_4_uid_com_m.powZn(lambda0iT.duplicate().mulZn(s_i));
        return H_4_uid_com_m;
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println(cause.toString());
        System.out.println("exceptionCaught: 发生异常，关闭通道");
        // 发生异常，关闭通道
        ctx.close();
    }
}
